package com.wzf.filter;

import com.alibaba.cloud.nacos.NacosServiceInstance;
import com.alibaba.nacos.common.utils.CollectionUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.*;
import org.springframework.cloud.loadbalancer.core.NoopServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.core.ReactorServiceInstanceLoadBalancer;
import org.springframework.cloud.loadbalancer.core.SelectedInstanceCallback;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import reactor.core.publisher.Mono;

import java.util.Comparator;
import java.util.List;
import java.util.Random;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Slf4j
public class CustomizeGrayLoadBalancer implements ReactorServiceInstanceLoadBalancer {
    final String serviceId;
    ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider;

    public CustomizeGrayLoadBalancer(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider, String serviceId) {
        this.serviceId = serviceId;
        this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider;
    }


    public Mono<Response<ServiceInstance>> choose(Request request) {
        RequestDataContext requestContext = (RequestDataContext)request.getContext();
        String version = requestContext.getClientRequest().getHeaders().getFirst("version");
        ServiceInstanceListSupplier supplier = (ServiceInstanceListSupplier)this.serviceInstanceListSupplierProvider.getIfAvailable(NoopServiceInstanceListSupplier::new);
        return supplier.get(request).next().map((serviceInstances) -> {
            return this.processInstanceResponse(supplier, serviceInstances,version);
        });
    }

    private Response<ServiceInstance> processInstanceResponse(ServiceInstanceListSupplier supplier, List<ServiceInstance> serviceInstances, String version) {
        if(StringUtils.isBlank(version)){
            version=Integer.MAX_VALUE +"";
        }
        Response<ServiceInstance> serviceInstanceResponse = this.getInstanceResponse(serviceInstances,Integer.valueOf(version));
        if (supplier instanceof SelectedInstanceCallback && serviceInstanceResponse.hasServer()) {
            ((SelectedInstanceCallback)supplier).selectedServiceInstance((ServiceInstance)serviceInstanceResponse.getServer());
        }

        return serviceInstanceResponse;
    }

    private Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> instances,Integer version) {
        if (instances.isEmpty()) {
            if (log.isWarnEnabled()) {
                log.warn("No servers available for service: " + this.serviceId);
            }

            return new EmptyResponse();
        } else {

            if (instances.size() == 1) {
                List<NacosServiceInstance> nacosInstances= instances.stream().map(e -> (NacosServiceInstance) e).collect(Collectors.toList());
                return new DefaultResponse(nacosInstances.get(0));
            } else {
                List<NacosServiceInstance> nacosInstances= instances.stream().map(e -> (NacosServiceInstance) e).collect(Collectors.toList());
                List<Integer> integers = nacosInstances.stream().map(e -> e.getMetadata().get(CustomizeLoadBalanceProperties.HEADER_VERSION_NAME)).
                        filter(StringUtils::isNotBlank).map(e -> Integer.valueOf(e)).collect(Collectors.toList());
                if(CollectionUtils.isEmpty(integers)){
                    NacosServiceInstance nacosServiceInstance = nacosInstances.get(RandomUtils.nextInt(0, instances.size()));
                    return new DefaultResponse(nacosServiceInstance);
                }
                Integer maxVersion = integers.stream().max(Comparator.naturalOrder()).get();
                //版本为空，或大于等于当前版本，选最大版本号
                if(version == null || version>=maxVersion){
                    List<NacosServiceInstance> collect = nacosInstances.stream().filter(e -> e.getMetadata().get(CustomizeLoadBalanceProperties.HEADER_VERSION_NAME).equals(maxVersion)).
                            collect(Collectors.toList());
                    NacosServiceInstance nacosServiceInstance = collect.get(RandomUtils.nextInt(0, instances.size()));
                    return new DefaultResponse(nacosServiceInstance);
                }
                List<NacosServiceInstance> collect = nacosInstances.stream().filter(e -> !e.getMetadata().get(CustomizeLoadBalanceProperties.HEADER_VERSION_NAME).equals(maxVersion)).
                        collect(Collectors.toList());

                NacosServiceInstance nacosServiceInstance = collect.get(RandomUtils.nextInt(0, collect.size()));
                return new DefaultResponse(nacosServiceInstance);
            }
        }
    }
}
